import datetime
import time
import secrets
import flask.logging
import pymysql
from flask import Flask, request
import json
from config import *
import mySqlFunc
import myLogger
import logging
from flask_cors import CORS

app = Flask(__name__)
sqlLogger = myLogger.initLogger(SQL_LOGPATH, "mysql")

CORS(app, supports_credentials=True)
def getSqlConn():
    return mySqlFunc.MysqlConnector(MYSQL_HOST, MYSQL_USER, MYSQL_PASSWORD, MYSQL_DATABASE, sqlLogger)


def checkSession():
    """
    检查WebSocket的Session是否合法
    :return 验证失败返回None, 成功返回uid
    """
    sqlConn = getSqlConn()
    # 获取并保存 cookie 信息
    sessionID = request.cookies.get("sessionID", None)
    if sessionID is None:
        app.logger.warning(f"sessionID not set: ip={request.remote_addr}")
        return None
    result = sqlConn.Select(f"select uid, ip, expire_time "
                            f"from {SESSION_TABLE} where session_id='{sessionID}'")

    # 没有此SessionID
    if len(result) != 1:
        app.logger.warning(f"sessionID not exist failed: sessionID={sessionID}, ip={request.remote_addr}")
        return None
    result = result[0]
    uid, ip, expireTime = result
    expireTime = int(expireTime.timestamp())

    # SessionID过期或不可用(IP地址更改) 返回错误并删除此Session
    if ip != request.remote_addr or expireTime < int(time.time()):
        app.logger.info(f"delete session: uid={result[0]}, sessionID={sessionID}")
        sqlConn.Execute(f"delete from {SESSION_TABLE} where session_id='{sessionID}'")
        return None

    app.logger.info(f"check session success: uid={result[0]}, ip={request.remote_addr}")
    return uid


def checkPriority(uid, priority):
    """
    检查用户权限
    :return 验证失败返回False, 成功返回True
    """
    sqlConn = getSqlConn()
    pri = sqlConn.Select(f"select priority from users where uid={uid}")
    if len(pri) != 1:
        return False
    pri = pri[0][0]
    if pri != priority:
        app.logger.warning(f"check priority failed: uid={uid}, ip={request.remote_addr}")
        return False
    app.logger.info(f"check priority success: uid={uid}, ip={request.remote_addr}")
    return True


@app.route("/login", methods=['post'])
def login():
    # 免密登录
    uid = checkSession()
    if uid is not None:
        if checkPriority(uid, PRIORITY_CLERKS):
            resp = flask.make_response(f"Welcome, admin!")  # 设置响应体
            return resp
        else:
            resp = flask.make_response(f"Welcome, user!")  # 设置响应体
            return resp
    # 获取参数并判断存在性
    uid = request.values.get('uid', None)
    password = request.values.get('password', None)
    ip = request.remote_addr
    if uid is None or password is None:
        app.logger.info(f"login failed(not enough parameters): uid={uid}, password={password}, ip={ip}")
        return f"login failed(not enough parameters)"

    # 尝试从数据库中获取对应uid
    sqlConn = getSqlConn()
    res = sqlConn.Select(f"select username from {USER_TABLE} where uid={uid} and password={password}")
    print(res)
    if len(res) != 1:
        app.logger.warning(f"login failed(uid or password not correct): uid={uid}, password={password}, ip={ip}")
        return "login failed(uid or password not correct)"
    res = res[0]
    username, = res

    # 存活时间为半小时
    expire_time = datetime.datetime.fromtimestamp(int(time.time()) + 60 * 30).strftime('%Y-%m-%d %H:%M:%S')
    sessionID = secrets.token_hex(16)
    if sqlConn.Insert(SESSION_TABLE, {"session_id": sessionID, "uid": uid, "ip": ip, "expire_time": expire_time}) != 1:
        app.logger.critical(f"Session Insert Failed: uid={uid}")
        raise Exception(f"Session Insert Failed: uid={uid}")
    app.logger.info(f"login and set session success: uid={uid}, ip={ip}")

    # 设置cookie
    resp = flask.make_response(f"Welcome, {username}!")  # 设置响应体
    resp.set_cookie("sessionID", sessionID, max_age=COOKIE_MAX_AGE)
    return resp


@app.route("/account/open", methods=['post'])
def openAccount():
    # 账户检查
    clerkUid = checkSession()
    if clerkUid is None:
        return "session expired"
    if not checkPriority(clerkUid, PRIORITY_CLERKS):
        return "low priority"

    # 获取并检查各项参数
    uid = request.values.get("uid", None)
    username = request.values.get("username", None)
    password = request.values.get("password", None)
    priority = PRIORITY_USERS
    balance = 0
    if uid is None or username is None or password is None:
        app.logger.error(f"Open account not enough parameters")
        return "Open account failed(not enough parameters)"

    # 添加账户到数据库中
    sqlConn = getSqlConn()
    try:
        sqlConn.Insert(USER_TABLE, {"uid": uid, "username": username, "password": password, "priority": priority,
                                    "balance": balance})
    except pymysql.Error:
        app.logger.warning(f"Insert account failed: uid={uid}, ip={request.remote_addr}")
        return "Open account failed(uid may be Conflicting)"
    return "success"


@app.route("/account/delete", methods=['post'])
def deleteAccount():
    # 账户检查
    clerkUid = checkSession()
    if clerkUid is None:
        return "session expired"
    if not checkPriority(clerkUid, PRIORITY_CLERKS):
        return "low priority"

    # 获取并检查各项参数
    uid = request.values.get("uid", None)
    if uid is None:
        app.logger.error(f"Delete account not enough parameters")
        return "not enough parameters"

    # 将账户从数据库中删除
    sqlConn = getSqlConn()
    affected = sqlConn.Execute(f"Delete from users where uid={uid}")
    if affected == 0:
        app.logger.warning(f"Delete account failed(uid not exist): uid={uid}, ip={request.remote_addr}")
        return "Delete account failed(uid not exist)"
    if affected > 1:
        app.logger.critical(f"Delete account more than one!"
                            f"affected: {affected} rows, uid={uid}, ip={request.remote_addr}")
        flask.abort(403)
    return "success"


@app.route("/balance/get", methods=['get'])
def getBalance():
    # 获取并检查各项参数
    uid = request.values.get('uid', None)
    if uid is None:
        return "not enough parameters"

    # 检查登录信息
    uid = int(uid)
    sessionUid = checkSession()
    if sessionUid is None:
        return "session expired"
    if uid != sessionUid and not checkPriority(sessionUid, PRIORITY_CLERKS):
        app.logger.warning(f"Get balance failed(not your account): uid={uid}, ip={request.remote_addr}")
        return "Get balance failed(not your account)"

    # 查找并返回结果
    sqlConn = getSqlConn()
    res = sqlConn.Select(f"select balance from {USER_TABLE} where uid={uid}")
    if len(res) == 0:
        app.logger.warning(f"Get balance failed(uid not exist): uid={uid}, ip={request.remote_addr}")
        return "Get balance failed(uid not exist)"
    app.logger.info(f"Get balance success: uid={uid}, ip={request.remote_addr}")
    return str(res[0][0])


@app.route("/balance/transfer", methods=['post'])
def transferBalance():
    # 获取并检查各项参数
    src_uid = request.values.get('src_uid', None)
    dst_uid = request.values.get('dst_uid', None)
    amount = request.values.get('amount', None)
    if src_uid is None or dst_uid is None or amount is None:
        return "not enough parameters"
    src_uid = int(src_uid)
    dst_uid = int(dst_uid)
    amount = int(amount)
    if amount <= 0:
        app.logger.warning(f"Transfer balance failed(amount should be positive): ip={request.remote_addr}")
        return "Transfer balance failed(amount should be positive)"

    # 检查登录信息和账户余额
    uid = checkSession()
    if uid is None:
        return "session expired"
    if (uid != src_uid or src_uid == dst_uid) and not checkPriority(uid, PRIORITY_CLERKS):
        app.logger.error(str(uid != src_uid) + str(src_uid == dst_uid) + str(checkPriority(uid, PRIORITY_CLERKS)))
        app.logger.warning(f"Transfer balance failed(not your account): uid={uid}, ip={request.remote_addr}")
        return "Transfer balance failed(not your account)"

    # 检查账户余额
    sqlConn = getSqlConn()
    res = sqlConn.Select(f"select balance from {USER_TABLE} where uid={src_uid}")
    if len(res) == 0:
        app.logger.warning(f"Get balance failed(src_uid not exist): uid={src_uid}, ip={request.remote_addr}")
        return "Transfer balance failed(src_uid not exist)"
    src_balance = res[0][0]
    if src_balance < amount:
        # 账户余额不足
        app.logger.warning(f"Transfer balance failed(Insufficient account balance): "
                           f"uid={src_uid}, ip={request.remote_addr}")
        return "Transfer balance failed(Insufficient account balance)"

    # 获取对方账户
    res = sqlConn.Select(f"select balance from {USER_TABLE} where uid={dst_uid}")
    if len(res) == 0:
        app.logger.warning(f"Get balance failed(dst_uid not exist): uid={dst_uid}, ip={request.remote_addr}")
        return "Transfer balance failed(dst_uid not exist)"
    dst_balance = res[0][0]

    # 设置新的钱数
    affected = sqlConn.Execute(f"update users set balance={src_balance - amount} where uid={src_uid}")
    affected += sqlConn.Execute(f"update users set balance={dst_balance + amount} where uid={dst_uid}")
    if affected != 2:
        app.logger.critical(f"transfer account affected not 2!"
                            f"affected: {affected} rows, uid={uid}, ip={request.remote_addr}")
        flask.abort(403)
    return "success"


def setLogger():
    app.logger.removeHandler(flask.logging.default_handler)
    app.logger.setLevel(logging.DEBUG)

    formatter = logging.Formatter('%(asctime)s - %(name)s - %(levelname)s - %(message)s')
    # fileHandler
    fileHandler = logging.FileHandler(APP_LOGPATH)
    fileHandler.setFormatter(formatter)
    fileHandler.setLevel(logging.DEBUG)
    app.logger.addHandler(fileHandler)

    # consoleHandler
    consoleHandler = logging.StreamHandler()
    consoleHandler.setLevel(logging.DEBUG)
    consoleHandler.setFormatter(formatter)
    app.logger.addHandler(consoleHandler)


if __name__ == '__main__':
    # 设置app的日志文件路径, 并将日志打印到命令行
    setLogger()
    app.run(host="0.0.0.0")
